@file:Suppress("unused")

package kotlinx.coroutines.script

import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.os.Build
import sample.ui.ConsoleLayout.Companion.outputText
import sample.ui.RectLayout
import sample.ui.Properties
import abc.reflect.App
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.script.analysis.Analysis
import kotlinx.coroutines.script.tes.*
import kotlinx.coroutines.withContext
import v9.assist.access.btnMYBLMenuFJ
import v9.assist.access.btnMYBLMenuJH
import v9.assist.captureScreenshot
import java.io.File
import kotlin.math.min
import kotlinx.coroutines.script.GComponents.Companion.needRefreshSkill
import kotlinx.coroutines.script.GComponents.Companion.skillRefeshingTs
import v9.assist.access.accAtkByKit
import v9.assist.access.accPathfinding
import v9.assist.analyst.anaLastHasEnemyBossNameTs
import v9.assist.analyst.angleByRoad
import v9.assist.analyst.bsBossBeside
import v9.assist.analyst.bsPlayerHp
import v9.assist.analyst.lastEliteEnemyBossTs
import v9.assist.analyst.lastEliteEnemyTs
import v9.assist.script.sLastFeature31Ts
import v9.assist.script.xAtkByBossBesideTs
import v9.assist.script.xLastUseMedicineTs
import v9.assist.script.xScriptStartTs
import java.util.concurrent.atomic.AtomicBoolean

class SkillButtonV2(
    val name: String,
    val bitmap: Bitmap?,
    val view: Block,
    val forceMaxTs: Long, // 技能最大等待时间. 30秒
    val longPressTs: Long,
    val userEnable: Int, // 0禁用. 1战斗. 2全程. 3仅Boss+精英. 4仅Boss
    val releaseType: Int, // 0好了就放. 1降频释放
) {
    companion object {
        val NONE = SkillButtonV2("", null, Block("", 0, 0, 0, 0), 0L, 0L, 0, 0)
        var lastClickSkillTs = 0L
        var nextAtkEnableTs = 0L
    }
    private val tapUnlimited get() = userEnable == 1
            || userEnable == 2
            // 仅Boss或精英
            || (userEnable == 3 && (System.currentTimeMillis() - lastEliteEnemyBossTs < 6000 || System.currentTimeMillis() - lastEliteEnemyTs < 6000))
            // 仅Boss释放
            || (userEnable == 4 && (System.currentTimeMillis() - lastEliteEnemyBossTs < 6000))

    private val tapAtkUnlimited get() = name == C.V_ATK
            && tapUnlimited
            && System.currentTimeMillis() > nextAtkEnableTs // 技能后的延迟

    @Volatile private var lastClickTs = 0L
    val lastClickedTs get() = lastClickTs
    @Volatile private var lastBitmapCompareTs = 0L
    private val bitmapUsable = AtomicBoolean(true)
    private var bitmapUsableTs = 0L // 上次技能可用的时间戳

    // atk hasEnemy 期间查询
    val usable: Boolean get() {
        if (userEnable == 0) return false
        if (tapAtkUnlimited) return true
        // // 等技能刷新.等3秒存一张图片应该是稳的.
        if (needRefreshSkill || System.currentTimeMillis() - skillRefeshingTs < 2500) return false
        if (System.currentTimeMillis() - lastClickTs < min(forceMaxTs, 1600L.shake(100))) return false
        if (bitmapUsable.get() && System.currentTimeMillis() > bitmapUsableTs && tapUnlimited) return true // 等技能可用时间戳到位再释放
        return forceMaxTs > 0 && System.currentTimeMillis() - lastClickTs > forceMaxTs && tapUnlimited
    }
    // 全程可用
    val usableAlways: Boolean get() {
        // 第三个避免初始化到错误的cd状态图
        return usable && userEnable == 2 && System.currentTimeMillis() - xScriptStartTs > 16000L
    }

    fun refreshByFrame(ss: Bitmap) {
        if (usable) return // 这里面把randomTs考虑进去了试试错误的
        if (bitmapUsable.get()) return

        if (System.currentTimeMillis() - lastBitmapCompareTs < 450L) return
        if (System.currentTimeMillis() - lastClickTs < 450L) return
        if (Analysis.compareSkillBitmap(ss, view, bitmap, 8.pt,0.45f)) { // "skill${name}" 有0.7
            lastBitmapCompareTs = System.currentTimeMillis()
            //  先设置时间 避免下面的usable卡住
            if (name == C.V_SKILL_A || name == C.V_SKILL_B || name == C.V_SKILL_C || name == C.V_SKILL_D) {
                bitmapUsableTs = if (releaseType == 0) System.currentTimeMillis() - 1
                else (System.currentTimeMillis() + (800L..4000L).random())
            }
            bitmapUsable.set(true)
        }
    }

    suspend fun useV2(fa: Int? = null) {
        val a = fa ?: angleByRoad
        val e = if (random.nextBoolean()) 70.shake(20) else (-70).shake(20)
        val angle = if (bsPlayerHp < 0.8f && a != null) a + e else a
        accAtkByKit(angle, name)
        lastClickTs = System.currentTimeMillis()
        if (name == C.V_SKILL_A || name == C.V_SKILL_B || name == C.V_SKILL_C || name == C.V_SKILL_D) {
            lastClickSkillTs = System.currentTimeMillis() // 释放了技能
            nextAtkEnableTs = lastClickSkillTs + (500L..1200L).random()
        }
        bitmapUsable.set(false)
    }

    private var lastSkillE: Bitmap? = null
    private var sameSkillE: Int = 0
    fun refreshByFrameE(ss: Bitmap) {
        if (usable) return
        if (System.currentTimeMillis() - lastBitmapCompareTs < 600L) return
        if (lastSkillE == null) {
            // do nothing
        } else if (Analysis.compareSkillBitmap(ss, view, lastSkillE, 8.pt,0.9f)) {
            sameSkillE++
            if (sameSkillE >= 20) printLog { "refreshByFrameE $sameSkillE" }
            if (sameSkillE > 24) {
                sameSkillE = 0
                bitmapUsable.set(true)
            }
            lastBitmapCompareTs = System.currentTimeMillis()
        } else sameSkillE = 0

        lastSkillE = clipBitmap(ss, view.rect)
    }
}

class GABPanelV2(
    val medicine: SkillButtonV2, // 加血
    val interact: SkillButtonV2,
    val atk: SkillButtonV2,
    val skillA: SkillButtonV2,
    val skillB: SkillButtonV2,
    val skillC: SkillButtonV2,
    val skillD: SkillButtonV2,
    val skillE: SkillButtonV2,
    val skillF800: SkillButtonV2,
    val skillF800B: SkillButtonV2,
) {
    fun refreshByFrame(ss: Bitmap) {
        // java.lang.IllegalArgumentException
        // width must be > 0
        if (ss.width == 0 || ss.height == 0) return //
        medicine.refreshByFrame(ss)
        skillA.refreshByFrame(ss)
        skillB.refreshByFrame(ss)
        skillC.refreshByFrame(ss)
        skillD.refreshByFrame(ss)
        skillE.refreshByFrameE(ss)
    }
}


private val atk get() =  GComponents.get().abPanel.atk
private val medicine get() = GComponents.get().abPanel.medicine
private val skillA get() =  GComponents.get().abPanel.skillA
private val skillB get() = GComponents.get().abPanel.skillB
private val skillC get() =  GComponents.get().abPanel.skillC
private val skillD get() = GComponents.get().abPanel.skillD
private val skillE get() = GComponents.get().abPanel.skillE
private val skillF800 get() = GComponents.get().abPanel.skillF800

val usableSkillAlways: SkillButtonV2? get() {
    val list = mutableListOf<SkillButtonV2>()
    if (skillA.usableAlways) list.add(skillA)
    if (skillB.usableAlways) list.add(skillB)
    if (skillC.usableAlways) list.add(skillC)
    if (skillD.usableAlways) list.add(skillD)
    return list.randomOrNull()
}

suspend fun accAutoAtk(fa: Int? = null) {
    val list = mutableListOf<SkillButtonV2>()
    if (skillA.usable) list.add(skillA)
    if (skillB.usable) list.add(skillB)
    if (skillC.usable) list.add(skillC)
    if (skillD.usable) list.add(skillD)
    if (skillE.usable) list.add(skillE)

    if (Properties.sceneSkill800 && skillF800.usable) list.add(skillF800)
    if (medicine.usable && bsPlayerHp < 0.8f && System.currentTimeMillis() - xLastUseMedicineTs > 6000) list.add(medicine)
    val s = list.randomOrNull() ?: (if (atk.usable) atk else null)

    s?.useV2(sceneFixedAngle(fa))

    if (s == null) accPathfinding(fa ?: angleByRoad)
    if (bsBossBeside) xAtkByBossBesideTs = System.currentTimeMillis()
}

private var lastUsingAtkAngle: Int? = null
private fun sceneFixedAngle(fa: Int?): Int? {
    if (Properties.scene == C.S_SM) {
        if (System.currentTimeMillis() - anaLastHasEnemyBossNameTs < 6000
            || System.currentTimeMillis() - lastEliteEnemyBossTs < 6000) {
            if (System.currentTimeMillis() - sLastFeature31Ts < 2300) {
                printLog { "AtkKit 神庙特殊化Boss战.保持移动方向" } // 2300时间挺好,刚好都躲避过去了
                return lastUsingAtkAngle
            } else sLastFeature31Ts = System.currentTimeMillis()
        }
    }
    lastUsingAtkAngle = fa
    return fa
}




class GComponents(
    val selfHp: Block,
    private val _sMap: Block,
    val abPanel: GABPanelV2,
//    val cMap: GMap,
) {
    private val _selfInSMap by lazy {
        Block("小地图中心", _sMap.x - 10.pt, _sMap.y - 10.pt, 20.pt, 20.pt)
    }
    private val _largeSMap by lazy {
        Block("大号小地图", _sMap.left - 150.pt, _sMap.top, _sMap.width + 146.pt, _sMap.height + 98.pt)
    }
    private val _selfInLargeSMap by lazy {
        Block(" 大号小地图中心", _largeSMap.x - 10.pt, _largeSMap.y - 10.pt, 20.pt, 20.pt)
    }
    val selfInSmallMap get() =   _selfInSMap
    val smallMap get() =  _sMap
    val smallMapCenter get() = Block("小地图中心" , smallMap.x - 100.pt , smallMap.y - 100.pt, 200.pt, 200.pt)

    val sMapCenter by lazy {
        Block("小地图中心", _sMap.x - 100.pt , _sMap.y - 100.pt, 200.pt, 200.pt)
    }
    val surroundSelfInsMap by lazy {
        Block("小地图中心身边", _selfInSMap.x - 40.pt, _selfInSMap.y - 40.pt, 80.pt, 80.pt)
    }
    val centerTop by lazy {
        Block("中心上方", phoneScreenWidth / 2 - 100.pt , 100.pt, 200.pt, 30.pt)
    }
    val mapGlassToOpen by lazy {
        Block("地图搜索展开",  selfHp.left + 40.pt, 490.pt, 30.pt, 30.pt)
    }
    val mapGlassOpened by lazy {
        Block("地图搜索(已展开)",  selfHp.left + 290.pt, 490.pt, 30.pt, 30.pt)
    }
    val mapGuideItem1 by lazy {
        Block("地图指引条目1", selfHp.left  + 120.pt, 260.pt, 100.pt, 30.pt)
    }
    val mapGoTarget by lazy {
        Block("地图前往此处", phoneScreenWidth / 2 - 50.pt , 610.pt, 100.pt, 30.pt)
    }
    val mapEssenceCheckPart by lazy {
        Block("精华菜单检测区域", selfHp.left + 59.pt, 240.pt, 36.pt, 600.pt)
    }

    val rocker by lazy {
//        Prefab.requirePrefab(Prefab.ITEM_ROCKER)?.parseToBlock() ?:
//        Block(C.V_ROCKER, selfHp.left + 320.pt, 790.pt, 40.pt, 20.pt)
//        Block(C.V_ROCKER, selfHp.left + 300.pt, 800.pt, 40.pt, 20.pt)
        Block(C.V_ROCKER, selfHp.left + 240.pt, 800.pt, 4.pt, 4.pt)
    }
    val interactRealTap by lazy {
        // 互动按钮真实点击区域
        Block("互动", abPanel.interact.view.left - 20.pt, abPanel.interact.view.top - 20.pt,
            abPanel.interact.view.width + 40.pt, abPanel.interact.view.height + 40.pt)
    }
    val lv by lazy {
        Block("LV", selfHp.left, selfHp.top - 25.pt, 60.pt, 20.pt) // 80往下找-25不会是负数
    }
    val menuPrimary by lazy {
        Block("主菜单", _sMap.right + 12.pt, 20.pt, 50.pt, 80.pt)
    }
    val menuCastleEnterClose by lazy {
        Block("古堡入口关闭", _sMap.right + 12.pt, 180.pt, 20.pt, 20.pt)
    }
    val menuCastleClearUpClose by lazy {
        Block("古堡铁匠分解关闭", _sMap.right + 12.pt, 30.pt, 20.pt, 20.pt)
    }
    val menuZDZF by lazy {
        Block("菜单战斗祝福", _sMap.right - 80.pt, 300.pt, 40.pt, 40.pt)
    }
    val menuZDZFCSZF by lazy {
        Block("传世祝福", phoneScreenWidth / 2 - 640.pt, 860.pt, 40.pt, 40.pt)
    }
    val menuZDZFCSZFTake by lazy {
        Block("获取贡献", phoneScreenWidth / 2 + 240.pt, 710.pt, 40.pt, 40.pt)
    }
    val menuZDZFCSZFTakeGo by lazy {
        Block("挑战魔神", phoneScreenWidth / 2 + 160.pt, 520.pt, 30.pt, 30.pt)
    }



    val menuPackage by lazy {
        Block("背包", _sMap.right + 22.pt, 135.pt, 40.pt, 70.pt)
    }
    val menuPackageTab3 by lazy {
        Block("背包材料", _sMap.right - 20.pt, 890.pt, 40.pt, 40.pt)
    }
    val menuPackageContentLine1Part by lazy {
        Block("背包材料第1行区域", _sMap.right - 600.pt, 130.pt, 640.pt, 50.pt)
    }
    val menuPackageContentLine2Part by lazy {
        Block("背包材料第2行区域", _sMap.right - 600.pt, 240.pt, 640.pt, 60.pt)
    }
    val menuPackageContentLine3Part by lazy {
        Block("背包材料第3行区域", _sMap.right - 600.pt, 350.pt, 640.pt, 60.pt)
    }


    val menuKeyToImmButton by lazy {
        Block("前往秘境", _sMap.right - 910.pt, 520.pt, 100.pt, 20.pt)
    }

    val mainMenu by lazy {
        Block("主菜单B", _sMap.right + 24.pt, 40.pt, 30.pt, 40.pt)
    }
    val groupQuickStart by lazy {
        Block("组队快速开始", phoneScreenWidth / 2 - 560.pt, 790.pt, 60.pt, 40.pt)
    }
    val groupMenu by lazy {
        Block("组队", _sMap.right - 320.pt, 880.pt, 60.pt, 40.pt)
    }
    val settingsMenu by lazy {
        Block("设置", _sMap.right - 110.pt, 40.pt, 40.pt, 40.pt)
    }

    val switchRole by lazy {
        Block("切换角色", _sMap.right - 240.pt, 900.pt, 100.pt, 20.pt)
    }
    val selectRole by lazy {
        Block("选择角色", _sMap.right - 240.pt, 900.pt, 100.pt, 20.pt)
    }
    val roleExitClose by lazy {
        Block("角色退出", _sMap.right , 30.pt, 20.pt, 20.pt)
    }
    val skippable by lazy {
        Block("跳过动画", _sMap.right + 8.pt, 45.pt, 18.pt, 32.pt)
    }
    val myblSubmitJH by lazy {
        Block(btnMYBLMenuJH, _sMap.right - 340.pt, 600.pt, 100.pt, 30.pt)
    }
    val myblSubmitFJ by lazy {
        Block(btnMYBLMenuFJ, _sMap.right - 340.pt, 510.pt, 100.pt, 30.pt)
    }
    val myblSubmitFJSubmit by lazy {
        Block("分解提交", _sMap.right - 340.pt, 860.pt, 100.pt, 30.pt)
    }
    val myblOpenState by lazy {
        Block("展开状态", selfHp.left + 80.pt, 260.pt, 20.pt, 20.pt)
    }
    val sceneHideState by lazy {
        Block("关闭副本状态", selfHp.left + 470.pt, 320.pt, 10.pt, 20.pt)
    }
    val sceneFlag by lazy {
        Block("副本中", _sMap.left - 65.pt, _sMap.bottom - 25.pt, 30.pt, 10.pt)
    }
    val largeSceneFlag by lazy {
        Block("副本中(大)", _largeSMap.left - 65.pt, _largeSMap.bottom - 25.pt, 30.pt, 10.pt)
    }

    val baseMenuLine1 by lazy {
        Block("主菜单第一行", _sMap.right - 120.pt, 160.pt, 120.pt, 20.pt)
    }
    val baseMenuLine2 by lazy {
        Block("主菜单第二行", baseMenuLine1.left, 300.pt, baseMenuLine1.width, baseMenuLine1.height)
    }
    val baseMenuLine3 by lazy {
        Block("主菜单第三行", baseMenuLine1.left, 440.pt, baseMenuLine1.width, baseMenuLine1.height)
    }

    val equipmentDecomposition by lazy {
        Block("装备分解",  _sMap.right - 470.pt, 600.pt, 400.pt, 50.pt)
    }
    val equipmentDecompositionCenter by lazy {
        Block("装备分解(中心)",  _sMap.right - 390.pt, 610.pt, 240.pt, 30.pt)
    }
    val buttonCastleEnterMode2 by lazy {
        Block("古堡无尽", _sMap.right - 280.pt, 280.pt, 240.pt, 40.pt)
    }
    @Deprecated("")
    val buttonCheckPart2Castle by lazy {
        Block("入口页面按钮检测",  _sMap.right - 280.pt, 770.pt, 280.pt, 40.pt)
    }
    val buttonCheckCastleMode by lazy { // 2个按钮之间的缝隙
        Block("古堡按钮模式", _sMap.left - 20.pt, 770.pt, 40.pt, 40.pt)
    }
    val buttonCheckPart2Imm by lazy {
        Block("不朽秘境入口按钮检测",  _sMap.right - 280.pt, 860.pt, 280.pt, 30.pt)
    }
    val tzNextLv by lazy {
        Block("挑战秘境层数提升",  _sMap.right, 400.pt, 40.pt, 40.pt)
    }
    val tzNextPreLv by lazy {
        Block("挑战秘境层数提升保底层数",  _sMap.right - 560.pt, 400.pt, 40.pt, 40.pt)
    }
    val buttonCheckPart2Tz by lazy {
        Block("挑战秘境入口按钮检测",  _sMap.right - 280.pt, 780.pt, 280.pt, 30.pt)
    }
    val equipmentDecompositionSubmit by lazy {
        // 左边限制在副本再开右边.避免误触到了.副本再开页面的左边的按钮把.取消了集结
        Block("分解",  _sMap.right - 280.pt, CComponents.equipmentDecompositionW.top - 10.pt, 100.pt, 20.pt)
    }

    val sceneJL4PlayerLeaderEnter by lazy {
        Block("激流队长入口",  _sMap.right - 350.pt, 760.pt, 300.pt, 60.pt)
    }
    val fishExchange by lazy {
        Block("兑换",  _sMap.right - 500.pt, 860.pt, 500.pt, 40.pt)
    }
    val fishToExchange by lazy {
        Block("兑换钓鱼成果", _sMap.right - 400.pt, 510.pt, 400.pt, 40.pt)
    }
    val fishShop by lazy {
        Block("进入商店", _sMap.right - 400.pt, 600.pt, 400.pt, 40.pt)
    }
    val fishShopCountAdd by lazy {
        Block("增加数量", _sMap.right, 780.pt, 20.pt, 20.pt)
    }
    val fishShopBuy by lazy {
        Block("购买", _sMap.right - 400.pt, 880.pt, 400.pt, 40.pt)
    }

    val sceneCompletedTitleBg by lazy {
        Block("地下城传送门标题背景", phoneScreenWidth / 2 - 500.pt, 170.pt, 1000.pt, 2.pt)
    }
    val sceneCompletedBottomBg by lazy {
        Block("地下城传送门底部背景", phoneScreenWidth / 2 - 500.pt, 840.pt, 1000.pt, 2.pt)
    }
    val immWordCenterButton by lazy {
        Block("不朽词条按钮", phoneScreenWidth / 2 - 200.pt, 850.pt, 400.pt, 40.pt)
    }
    val immWordSkipButton by lazy {
        Block("不朽词条跳过", selfHp.right + 100.pt, 850.pt, 200.pt, 40.pt)
    }
//    val immDailyTitleBg by lazy {
//        Block("每日签到弹窗背景", selfHp.right + 100.pt, 850.pt, 200.pt, 40.pt)
//    }
    val immDailyButton by lazy {
        Block("每日签到按钮", phoneScreenWidth / 2 - 180.pt, 758.pt, 360.pt, 40.pt)
    }
    val immKeyA by lazy {
        Block("秘境钥匙A",  smallMap.right - 400.pt, 420.pt, 300.pt, 40.pt)
    }
    val immKeyB by lazy {
        Block("秘境钥匙B",  smallMap.right - 780.pt, 420.pt, 300.pt, 40.pt)
    }
    val immKeyCancel by lazy {
        Block("秘境钥匙取消", smallMap.left - 80.pt, 450.pt, 160.pt, 40.pt)
    }
    val applyRedDot by lazy {
        Block("是否有人申请", selfHp.right + 370.pt, 20.pt, 40.pt, 40.pt)
    }
    val applyMenu by lazy {
        Block("申请按钮", selfHp.right + 350.pt, 40.pt, 40.pt, 40.pt)
    }
    val applyPopMenu by lazy {
        Block("申请列表弹出菜单.申请列表", applyMenu.left + 60.pt, 320.pt, 300.pt, 40.pt)
    }
    val applyFirstName by lazy {
        Block("申请列表1昵称", phoneScreenWidth / 2 - 520.pt, 360.pt, 300.pt, 40.pt)
    }
    val applyRefuse by lazy {
        // Block("申请列表1昵称拒绝", phoneScreenWidth / 2 + 380.pt, 360.pt, 40.pt, 40.pt)
        // todo 版本bug
        applyRefuseAll
    }
    val applyAccept by lazy {
        // Block("申请列表1昵称同意", phoneScreenWidth / 2 + 530.pt, 360.pt, 40.pt, 40.pt)
        // todo 版本bug
        applyAcceptAll
    }
    val applyAcceptAll by lazy {
        Block("申请列表同意全部", phoneScreenWidth / 2 + 320.pt, 770.pt, 100.pt, 40.pt)
    }
    val applyRefuseAll by lazy {
        Block("申请列表拒绝全部", phoneScreenWidth / 2 - 420.pt, 770.pt, 100.pt, 40.pt)
    }
    @Deprecated("似乎不需要") val applyClose by lazy {
        Block("申请列表关闭", phoneScreenWidth / 2 + 735.pt, 140.pt, 30.pt, 30.pt)
    }

    val petPackMenu by lazy {
        Block("宠物菜单", _sMap.right - 522.pt, 900.pt, 20.pt, 20.pt)
    }
    val petPackBlacksmith by lazy {
        Block("宠物分解", petPackMenu.x - 40.pt, 655.pt, 80.pt, 20.pt)
    }
    val petEquipmentDecompositionW by lazy {
        Block("宠物分解白",  _sMap.right - 190.pt, 910.pt, 20.pt, 30.pt)
    }
    val petEquipmentDecompositionB by lazy {
        Block("宠物分解蓝",  _sMap.right - 88.pt, 910.pt, 20.pt, 30.pt)
    }
    val petEquipmentDecompositionY by lazy {
        Block("宠物分解黄",  _sMap.right + 2.pt, 910.pt, 20.pt, 30.pt)
    }
//    val petEquipmentDecompositionSubmit  by lazy {
//        Block("宠物分解提交",  sMap.right - 1100.pt, 860.pt, 100.pt, 30.pt)
//    }
    val petEquipmentDecompositionSubmit2  by lazy {
        // 应该再屏幕中间往左边一点点
        Block("宠物分解提交2",  _sMap.right - 1000.pt, 860.pt, 50.pt, 30.pt)
    }

    val groupName by lazy {
        Block("工会名称", selfHp.left + 500.pt, 120.pt, 200.pt, 40.pt)
    }
    val pageRootServerClose by lazy {
        Block("推荐服务器(再来一把)关闭", _sMap.right - 70.pt, 150.pt, 40.pt, 40.pt)
    }
//    val pageRootServerBottomBg by lazy {
//        Block("推荐服务器底部", phoneScreenWidth / 2 - 200.pt, 840.pt, 400.pt, 20.pt)
//    }
    val sceneMYMZStart by lazy {
        Block("魔域迷踪开始", phoneScreenWidth / 2 - 140.pt, 850.pt, 280.pt, 40.pt)
    }

    val sceneAtvSuperPkg by lazy {
        menuPrimary
    }
    val mainMenuXianZu by lazy {
        Block("先祖手记", smallMap.right - 300.pt, 580.pt, 100.pt, 40.pt)
    }
    val mainMenuXianZuBaoDianA by lazy {
        Block("活动宝典A", selfHp.right, 330.pt, 200.pt, 40.pt) // 还没有完成启程之章
    }
    val mainMenuXianZuBaoDianB by lazy {
        Block("活动宝典B", selfHp.right, 260.pt, 200.pt, 40.pt)
    }
    val mainMenuXianZuBaoDianTopA by lazy {
        Block("活动宝典置顶", smallMap.right - 70.pt, 490.pt, 40.pt, 40.pt)
    }
    val mainMenuXianZuBaoDianTopB by lazy {
        Block("活动宝典置顶2", smallMap.right - 70.pt, 810.pt, 40.pt, 40.pt)
    }
    val mainMenuLXDot by lazy {
        Block("菜单.裂隙", selfHp.left + 80.pt, 240.pt, 20.pt, 30.pt)
    }
    val btnLXUse by lazy {
        Block("裂隙使用", phoneScreenWidth / 2 - 80.pt, 590.pt, 160.pt, 40.pt)
    }
    val btnLxAtvTipClose by lazy {
        Block("裂隙弹窗活动关闭", phoneScreenWidth / 2 + 750.pt, 140.pt, 40.pt, 40.pt)
    }
    val checkLXProgress by lazy {
        Block("裂隙进度", selfHp.left + 40.pt, 404.pt, 80.pt, 16.pt)
    }
    val infoLxEnableCount by lazy {
        Block("裂隙可摧毁", selfHp.left + 120.pt, 438.pt, 20.pt, 30.pt)
    }
    val lxProgress1 by lazy {
        Block("裂隙进度1", selfHp.left + 40.pt, 364.pt, 330.pt, 20.pt)
    }
    val lxProgress2 by lazy { // 塔已全出现
        Block("裂隙进度2", selfHp.left + 40.pt, 400.pt, 330.pt, 20.pt)
    }
    val sceneAtvSuperTeam by lazy {
        Block("组队", smallMap.right - 300.pt, 880.pt, 100.pt, 40.pt)
    }
    val sceneAtvSuperTeamStart by lazy {
        Block("快速匹配", phoneScreenWidth / 2 - 580.pt, 790.pt, 200.pt, 40.pt)
    }
    val sceneAtvRDStart by lazy {
        Block("快速匹配开始", smallMap.right - 420, 770.pt, 160.pt, 40.pt)
    }
    val sceneEnterGap by lazy {
        Block("接受按钮间隙", smallMap.right - 335.pt, 760.pt, 30.pt, 60.pt)
    }
    val hpP2 by lazy {
        Block("玩家2号HP", selfHp.right + 24.pt, 89.pt, 60.pt, 10.pt)
    }
    val hpP3 by lazy {
        Block("玩家3号HP", selfHp.right + 138.pt, 89.pt, 60.pt, 10.pt)
    }
    val hpP3Avatar by lazy {
        Block("玩家3号头像", selfHp.right + 150.pt, 40.pt, 30.pt, 30.pt)
    }
    val hpP3Follow by lazy {
        Block("玩家3号跟随", hpP3.right + 40.pt, 350.pt, 200.pt, 40.pt)
    }
    val hpP4 by lazy {
        Block("玩家4号HP", selfHp.right + 244.pt, 89.pt, 60.pt, 10.pt)
    }
    val beKick by lazy {
        Block("被踢出确认", phoneScreenWidth / 2 - 50.pt, 590.pt, 100.pt, 40.pt)
    }
    val farmAutoAtk by lazy {
        Block("自动战斗", selfHp.left + 16.pt, 910.pt, 20.pt, 20.pt)
    }
    val farmAutoAtkCancel by lazy {
        Block("自动战斗取消", phoneScreenWidth / 2 + 170.pt, 630.pt, 30.pt, 30.pt)
    }
    val farmAutoStartConfirm by lazy {
        Block("自动战斗确认", phoneScreenWidth / 2 - 100.pt, 590.pt, 200.pt, 20.pt)
    }
    val sceneOpenState by lazy {
        Block("副本进度展开显示", selfHp.left + 90.pt, 330.pt , 20.pt, 20.pt)
    }
    val mapGBWJBlacksmith by lazy {
        Block("古堡铁匠分解", phoneScreenWidth / 2 + 300.pt, 655.pt, 20.pt, 20.pt)
    }
    val mapGBWJBlacksmithSubmit by lazy {
        Block("古堡铁匠分解确认", phoneScreenWidth / 2 + 260.pt, 760.pt, 80.pt, 30.pt)
    }
    val mapGBWJSceneEnter by lazy {
        Block("古堡进入", phoneScreenWidth / 2 + 10.pt, 760.pt, 20.pt, 20.pt)
    }
    val mapGBWJSceneEnterSubmit by lazy {
        Block("古堡进入确认", phoneScreenWidth / 2 - 20.pt, 850.pt, 80.pt, 30.pt)
    }
    val closeSceneMap by lazy {
        Block("关闭副本地图弹窗", smallMap.right - 40.pt, 0.pt, 110.pt, 154.pt)
    }
    val closeCheckPart by lazy {
        // Block("关闭检测",  phoneScreenWidth - 450.pt, 0.pt, 450.pt, 250.pt)
        // 大部分真按钮的高度是20.pt到80.pt.  副本再开是160.pt到220.pt
//        Block("关闭检测2",  phoneScreenWidth - 450.pt, 0.pt, 450.pt, 118.pt)
        // 到副本再开上面  涵盖申请列表是。 不要随便改宽度.不然别的依赖 closeable判断的逻辑会坏
        val left = min(smallMap.right - 220.pt, phoneScreenWidth - (phoneScreenWidth - 1400.pt) / 2)
        val width = phoneScreenWidth - left - 1
//        // closeCheckPart left [1951][1956] 打印这2个结果.这手机差不多
        Block("关闭检测2",  left, 0.pt, width, 154.pt)
    }
    val closeCheckPartForSweeping by lazy {
        Block("关闭检测2打野带隐秘",  phoneScreenWidth - 450.pt, 0.pt, 450.pt, 250.pt)
    }
//    val checkHasNpcAssist by lazy {
//        Block("一键援助", selfHp.left + 640.pt, 780.pt, 60.pt, 40.pt)
//    }
//    val checkHasNpcAssistUsed by lazy {
//        Block("一键援助(已使用)", selfHp.left + 745.pt, 780.pt, 60.pt, 40.pt)
//    }
    val autoDYBtnCheck by lazy {
        Block("自动打野按钮检测", selfHp.left + 30.pt, 900.pt, 30.pt, 30.pt)
    }
    val inScene by lazy {
        Block("副本内退出按钮", _sMap.left - 70.pt, 166.pt, 30.pt, 50.pt)
    }
    val fastAgainBtn by lazy {
        Block("随机快速再次开始", _sMap.right - 24.pt, 308.pt, 24.pt, 24.pt)
    }
    val fastAgainCancelBtn by lazy {
        Block("随机快速再次开始(取消)", _sMap.right - 104.pt, 308.pt, 24.pt, 24.pt)
    }
    val sceneEnterClose by lazy {
        Block("副本进入关闭", _sMap.right, 190.pt, 40.pt, 40.pt)
    }
    val mapToHomeBtn by lazy {
        Block("返回威斯特玛唤起按钮", _sMap.right - 55.pt, 825.pt, 20.pt, 20.pt)
    }
    val mapToHomeSubmitBtn by lazy {
        Block("返回维斯特玛确定", _sMap.right - 360.pt, 745.pt, 100.pt, 20.pt)
    }

    val widgetsWithoutMap: MutableList<Block> by lazy {
        val out = mutableListOf<Block>()
        out.add(abPanel.medicine.view)
        out.add(abPanel.atk.view)
        out.add(abPanel.skillA.view)
        out.add(abPanel.skillB.view)
        out.add(abPanel.skillC.view)
        out.add(abPanel.skillD.view)
        out.add(abPanel.skillE.view)
//        out.add(abPanel.skillF800.view)
//        out.add(abPanel.skillF800B.view)
//        out.add(abPanel.interact.view)
//        out.add(smallMap)
        out.add(selfHp)
        out.add(selfInSmallMap)
        out.add(rocker)
        out.add(CComponents.centerLine) // 这个线是有用的


        out
    }
    val widgetsInMap by lazy {
        val out = mutableListOf<Block>()
//        out.add(cMap.mapGlass)
//        out.add(cMap.placeClearPointMark)
//        out.add(cMap.checkMapBack)
        out
    }

    companion object {
        data class InitResult(val gc: GComponents?)
        val snapshot get() = File(FileUtils.filesDir("analysis/components"), "snapshot.png")
        val fileAtk get() = File(FileUtils.filesDir("analysis/components"), "sAtk.png")
        val fileM get() = File(FileUtils.filesDir("analysis/components"), "m.png")
        val fileSA get() = File(FileUtils.filesDir("analysis/components"), "sa.png")
        val fileSB get() = File(FileUtils.filesDir("analysis/components"), "sb.png")
        val fileSC get() = File(FileUtils.filesDir("analysis/components"), "sc.png")
        val fileSD get() = File(FileUtils.filesDir("analysis/components"), "sd.png")


        val isInitialized: Boolean get() = ins != null
        private var ins: GComponents? = null

        suspend fun initialize(): InitResult {
            printLog { "GComponents createInstance $ins" }
            val i = ins
            if (i != null) return InitResult(i)
//            if (!App.take().tas()) return InitResult(null)
            val initResult = createInstance()
            printLog { "GComponents createInstance result ${initResult.gc != null}" }
            ins = initResult.gc
            if (ins != null) {
                if (Properties.displayLog) {
                    toast1("请简单核对小地图是否有巨大偏差")
                    RectLayout.displayByInitCompleted(appContext)
                    delay(3500)
                    RectLayout.hide()
                    delay(1000)
                } else {
                    delay(100)
                }
            }


            return initResult
        }
        private val fakeGComponents by lazy {
            GComponents(
                Block("fakeSelfHp", 1, 1, 1, 1),
                Block("fakesMap", 1, 1, 1, 1),
                GABPanelV2(SkillButtonV2.NONE, SkillButtonV2.NONE, SkillButtonV2.NONE, SkillButtonV2.NONE,
                    SkillButtonV2.NONE, SkillButtonV2.NONE, SkillButtonV2.NONE,
                    SkillButtonV2.NONE, SkillButtonV2.NONE, SkillButtonV2.NONE),
            )
        }

        fun get(): GComponents {
            val curr = ins
            if (curr != null) return curr

            val cacheIns = createByCache()
            if (cacheIns != null) {
                ins = cacheIns
                // printLog { "GComponents get cache" }
                return cacheIns
            }
            // printLog { "GComponents get fake" }
            return fakeGComponents
        }
        var skillRefeshingTs = 0L
        var needRefreshSkill: Boolean = true
        val skillPrepared get() = !needRefreshSkill
        fun markNeedRefreshSkill() {
            needRefreshSkill = true
        }
        fun syncRefreshSkillIfNeed(ss: Bitmap) {
            if (needRefreshSkill) {
                needRefreshSkill = false
                skillRefeshingTs = System.currentTimeMillis()

                clipBitmap(ss, get().abPanel.medicine.view.rect)?.also {
                    FileUtils.syncWriteBitmapToDisk(fileM, it)
                }
                clipBitmap(ss, get().abPanel.atk.view.rect)?.also {
                    FileUtils.syncWriteBitmapToDisk(fileAtk, it)
                }
                clipBitmap(ss, get().abPanel.skillA.view.rect)?.also {
                    FileUtils.syncWriteBitmapToDisk(fileSA, it)
                }
                clipBitmap(ss, get().abPanel.skillB.view.rect)?.also {
                    FileUtils.syncWriteBitmapToDisk(fileSB, it)
                }
                clipBitmap(ss, get().abPanel.skillC.view.rect)?.also {
                    FileUtils.syncWriteBitmapToDisk(fileSC, it)
                }
                clipBitmap(ss, get().abPanel.skillD.view.rect)?.also {
                    FileUtils.syncWriteBitmapToDisk(fileSD, it)
                }
//                FileUtils.syncWriteBitmapToDisk(snapshot, ss)
                skillRefeshingTs = System.currentTimeMillis()
                printLog { "智能识别技能已更新" }
               // toast1("智能识别技能已更新")
                if (ins != null) {
                    val newSkill = createByCache()
                    if (newSkill != null) {
                        ins = newSkill
                        printLog { "技能已更新" }
                       // toast1("技能已更新")
                    }
                }
            }
        }

        fun clearGoDisk() { //
            writeToDisk("GOSelfHpLeft", null)
            writeToDisk("GOSelfHpRight", null)
            writeToDisk("GOSelfHpTop", null)
            writeToDisk("GOSelfHpBottom", null)
            writeToDisk("GOsMapLeft", null)
            writeToDisk("GOsMapRight", null)
            writeToDisk("GOsMapTop", null)
            writeToDisk("GOsMapBottom", null)
            writeToDisk("GOMedicineLeft2", null)
            writeToDisk("GOMedicineRight2", null)
            writeToDisk("GOMedicineTop2", null)
            writeToDisk("GOMedicineBottom2", null)
            writeToDisk("GOMapLeft", null)
            writeToDisk("GOMapRight", null)
            writeToDisk("GOMapTop", null)
            writeToDisk("GOMapBottom", null)

            writeToDisk("sceneEnterBlockLeft", null)
            writeToDisk("sceneEnterBlockTop", null)
            writeToDisk("sceneEnterBlockWidth", null)
            writeToDisk("sceneEnterBlockHeight", null)

            ins = null
            appScopeLaunch(Dispatchers.Main) {
                delay(800L) // 遮挡了上一个toast
                toast1("开始失败.已触发->清除所有缓存参数.可重新开始识别.\n注意地图.角色血条满.血瓶不空.连续出现点大退重开试试")
            }
        }

        @SuppressLint("QueryPermissionsNeeded")
        private fun hasVirus(): Boolean {
            return false
//            try {
//                appContext.packageManager.getInstalledPackages(0).forEach {
//                    if (it.packageName.contains("netguard")) {
//                        return true
//                    }
//                }
//            } catch (e:Throwable) {
//                e.printStackTrace()
//            }
//            return false
        }


        private suspend fun createInstance(): InitResult {
            val cache = createByCache()
            if (cache != null) return InitResult(cache)
            if (hasVirus()) return InitResult(null)
            return create().let {
                if (it == null) {
                    Controller.get().stop("")
                    clearGoDisk()
                }
                InitResult(it)
            }
        }

        private fun createByCache(): GComponents? {
            val selfHpLeft = takeInitParam("GOSelfHpLeft")
            val selfHpRight = takeInitParam("GOSelfHpRight")
            val selfHpTop = takeInitParam("GOSelfHpTop")
            val selfHpBottom = takeInitParam("GOSelfHpBottom")
            val selfHp = if (selfHpLeft != null && selfHpTop != null && selfHpRight != null && selfHpBottom != null) {
                Block(C.V_PLAYER_HP, selfHpLeft, selfHpTop, selfHpRight - selfHpLeft, selfHpBottom - selfHpTop)
            } else null

            if (selfHp == null) {
                // printLog { "GO createByCache selfHp null" }
                return null
            }
            val sMapLeft = takeInitParam("GOsMapLeft")
            val sMapRight = takeInitParam("GOsMapRight")
            val sMapTop = takeInitParam("GOsMapTop")
            val sMapBottom = takeInitParam("GOsMapBottom")
            val sMap = if (sMapLeft != null && sMapRight != null && sMapTop != null && sMapBottom != null) {
                Block(C.SMALL_MAP, sMapLeft, sMapTop, sMapRight - sMapLeft, sMapBottom - sMapTop)
            } else null
            if (sMap == null) {
                // printLog { "GO createByCache sMap null" }
                return  null
            }
            val medicineLeft = takeInitParam("GOMedicineLeft2")
            val medicineRight = takeInitParam("GOMedicineRight2")
            val medicineTop = takeInitParam("GOMedicineTop2")
            val medicineBottom = takeInitParam("GOMedicineBottom2")
            val medicine = if (medicineTop != null && medicineBottom != null && medicineLeft != null && medicineRight != null) {
                Block(C.V_MEDICINE, medicineLeft, medicineTop, medicineRight - medicineLeft, medicineBottom - medicineTop)
            } else null

            if (medicine == null) {
                // printLog { "GO createByCache medicine null" }
                return null
            }
//            val screenshot = FileUtils.syncReadBitmapFromDisk(snapshot)
//            if (screenshot == null) {
//                printLog { "GO createByCache screenshot null" }
//                return InitResult(null)
//            }
            val abPanel = gabPanelV2Provide(null, medicine)
            if (abPanel == null) {
                printLog { "GO createByCache abPanel null" }
                return null
            }
//            val map = if (
//                type?.contains("古堡") == true ||
//                type?.contains("副本") == true ||
//                type?.contains("钓鱼") == true) {
//                GMap.NONE
//            } else {
//                val mapLeft = readByDisk("GOMapLeft")?.toIntOrNull()
//                val mapRight = readByDisk("GOMapRight")?.toIntOrNull()
//                val mapTop = readByDisk("GOMapTop")?.toIntOrNull()
//                val mapBottom = readByDisk("GOMapBottom")?.toIntOrNull()
//                if (mapTop != null && mapBottom != null && mapLeft != null && mapRight != null) {
//                    Block("地图", mapLeft, mapTop, mapRight - mapLeft, mapBottom - mapTop)
//                } else null
//            }
//            val mapLeft = readByDisk("GOMapLeft")?.toIntOrNull()
//            val mapRight = readByDisk("GOMapRight")?.toIntOrNull()
//            val mapTop = readByDisk("GOMapTop")?.toIntOrNull()
//            val mapBottom = readByDisk("GOMapBottom")?.toIntOrNull()
//            val map = if (mapTop != null && mapBottom != null && mapLeft != null && mapRight != null) {
//                Block("地图", mapLeft, mapTop, mapRight - mapLeft, mapBottom - mapTop)
//            } else null
//            if (map == null) {
//                printLog { "GO createByCache map null" }
//                return InitResult(null)
//            }
            return GComponents(selfHp, sMap, abPanel) //InitResult() // , GMap(map)
        }

        private suspend fun create(): GComponents? {
//            val ssInit = Screenshot.get().ss ?: return InitResult(null, "游戏内容(请大退重启辅助)A").also { toast1("游戏内容识别失败(请大退重启辅助)A.截屏服务异常") }
            val ssInit = captureScreenshot() ?: return (null).also { toast1("截屏服务异常(请大退重启辅助)") }
            val selfHp = initializerFindSelfHp(ssInit) ?: return (null).also {
                toast1("血条识别失败(加满血.勿遮挡血条)(实际截屏尺寸:${ssInit.width}x${ssInit.height})")
            }
            CaptureInit.tryFix( selfHp) // 向左移动一步
            val ssAfterMoveToLeft = captureScreenshot() ?: return (null).also { toast1("截屏服务异常B(请大退重启辅助)") }

            val useLargeSMap = false
            val sMap = initializerFindsMap(ssAfterMoveToLeft) ?: return (null).also {
                val isMu12 = Build.VERSION.SDK_INT == Build.VERSION_CODES.S_V2
                        && (File("sys/class/net/eth0/address").exists() || File("sys/class/net/wlan0/address").exists())
                val tip = "地图识别失败(请调整游戏->设置->界面->小地图设置->${if(useLargeSMap)"大" else "小"}模式) ${if (isMu12) "(MuMu模拟器 右上角键鼠模式->选择无方案)" else ""}"
                toast1(tip)
            }
            val medicine = initializerFindMedicine(ssAfterMoveToLeft) ?: return (null).also {
                toast1("恢复药水识别失败.加满血瓶(勿遮挡)B")
                if (Build.VERSION.SDK_INT == Build.VERSION_CODES.S_V2
                    && (File("sys/class/net/eth0/address").exists() || File("sys/class/net/wlan0/address").exists())) {
                    // MuMu12药水识别失败
                    repeat(2) {
                        toast1("MuMu12模拟器需切换键鼠方案为鼠标无方案(点击右上角键盘图标)$it")
                        delay(1000)
                    }
                }
            }
            val abPanel = gabPanelV2Provide(ssAfterMoveToLeft, medicine) ?: return null
            // 应该不用不处理注释.过段时间删除
            // 先这样，开关一次 GestureCenter().click("closeMap", map.right - 25.pt, max(10.pt, map.top - 20.pt), widgetClickInterval = 800)
            printLog { "GO create $selfHp $sMap $medicine" } // $map"
            return GComponents(selfHp, sMap, abPanel) // , GMap(map)
        }


        private fun gabPanelV2Provide(ss: Bitmap?, medicine: Block): GABPanelV2? {
            val interact = Block(C.V_INTERACT, medicine.right - 420.pt, medicine.top + 77.pt, 50.pt, 50.pt)
            val atk = Block(C.V_ATK, medicine.right - 215.pt, medicine.top + 285.pt, 160.pt, 160.pt)
            val skillA = Block(C.V_SKILL_A, medicine.right - 355.pt, medicine.top + 385.pt, 90.pt, 90.pt)
            val skillB = Block(C.V_SKILL_B, medicine.right - 355.pt, medicine.top + 240.pt, 90.pt, 90.pt)
            val skillC = Block(C.V_SKILL_C, medicine.right - 250.pt, medicine.top + 135.pt, 90.pt, 90.pt)
            val skillD = Block(C.V_SKILL_D, medicine.right - 105.pt, medicine.top + 135.pt, 90.pt, 90.pt)
            val skillE = Block(C.V_SKILL_E, medicine.right - 495.pt, medicine.top + 240.pt, 90.pt, 90.pt)
            val skillF = Block(C.V_SKILL_F_800, medicine.right - 10.pt, medicine.top + 438.pt, 50.pt, 50.pt)
            val skillFB = Block(C.V_SKILL_F_800_B, medicine.right - 10.pt, medicine.top + 248.pt, 50.pt, 50.pt)
            //  val size = 95.pt->50.pt 移动45/2=22.5
            fun check(block: Block): Boolean {
                if (block.left < 0 || block.right > phoneScreenWidth|| block.top < 0 || block.bottom > phoneScreenHeight) {
                    printLog { "${block.name}组件初始化失败[${block.left}#${block.rect}]" }
                    toast("[${block.name}]初始化失败")
                    return false
                }
                return true
            }

            if (!check(interact)) return null
            if (!check(atk)) return null
            if (!check(skillA)) return null
            if (!check(skillB)) return null
            if (!check(skillC)) return null
            if (!check(skillD)) return null
            if (!check(skillE)) return null
            if (!check(skillF)) return null
            if (!check(skillFB)) return null

            val scheme = Properties.gabPanelPlan
            printLog { "create GabPanel by scheme [${ss?.width}x${ss?.height}]: $scheme" }

            val bmpMedicine = (if (ss != null) clipBitmap(ss, medicine.rect) else FileUtils.syncReadBitmapFromDisk(fileM))
            val bmpAtk = (if (ss != null) clipBitmap(ss, atk.rect) else FileUtils.syncReadBitmapFromDisk(fileAtk))
            val bmpSkillA = (if (ss != null) clipBitmap(ss, skillA.rect) else FileUtils.syncReadBitmapFromDisk(fileSA))
            val bmpSkillB = (if (ss != null) clipBitmap(ss, skillB.rect) else FileUtils.syncReadBitmapFromDisk(fileSB))
            val bmpSkillC = (if (ss != null) clipBitmap(ss, skillC.rect) else FileUtils.syncReadBitmapFromDisk(fileSC))
            val bmpSkillD = (if (ss != null) clipBitmap(ss, skillD.rect) else FileUtils.syncReadBitmapFromDisk(fileSD))
            // save
            bmpMedicine?.also { FileUtils.syncWriteBitmapToDisk(fileM, it) }
            bmpAtk?.also { FileUtils.syncWriteBitmapToDisk(fileAtk, it) }
            bmpSkillA?.also { FileUtils.syncWriteBitmapToDisk(fileSA, it) }
            bmpSkillB?.also { FileUtils.syncWriteBitmapToDisk(fileSB, it) }
            bmpSkillC?.also { FileUtils.syncWriteBitmapToDisk(fileSC, it) }
            bmpSkillD?.also { FileUtils.syncWriteBitmapToDisk(fileSD, it) }

            val sMedicine = SkillButtonV2(C.V_MEDICINE, bmpMedicine, medicine, -1, 0, 1, 0)
            val sInteract = SkillButtonV2(C.V_INTERACT, null, interact, -1, 0, 1, 0)
            val sAtk = SkillButtonV2(atk.name, bmpAtk, atk, scheme.atk.userCd, scheme.atk.userLongPressTs, scheme.atk.enable, scheme.atk.releaseType)
            val sSkillA = SkillButtonV2(skillA.name, bmpSkillA, skillA, scheme.skillA.userCd, scheme.skillA.userLongPressTs, scheme.skillA.enable, scheme.skillA.releaseType)
            val sSkillB = SkillButtonV2(skillB.name, bmpSkillB, skillB, scheme.skillB.userCd, scheme.skillB.userLongPressTs, scheme.skillB.enable, scheme.skillB.releaseType)
            val sSkillC = SkillButtonV2(skillC.name, bmpSkillC, skillC, scheme.skillC.userCd, scheme.skillC.userLongPressTs, scheme.skillC.enable, scheme.skillC.releaseType)
            val sSkillD = SkillButtonV2(skillD.name, bmpSkillD, skillD, scheme.skillD.userCd, scheme.skillD.userLongPressTs, scheme.skillD.enable, scheme.skillD.releaseType)
            val sSkillE = SkillButtonV2(skillE.name, null, skillE, scheme.skillE.userCd, scheme.skillE.userLongPressTs, scheme.skillE.enable, scheme.skillE.releaseType)
            val sSkillF = SkillButtonV2(skillF.name, null, skillF, scheme.skillF800.userCd, scheme.skillE.userLongPressTs, scheme.skillE.enable, scheme.skillF800.releaseType)
            val sSkillF800B = SkillButtonV2(skillFB.name, null, skillFB, scheme.skillF800b.userCd, scheme.skillE.userLongPressTs, scheme.skillE.enable, scheme.skillF800b.releaseType)
            printLog { "created GabPanelV2 ss ${ss != null}" }
            return GABPanelV2(sMedicine, sInteract, sAtk, sSkillA, sSkillB, sSkillC, sSkillD, sSkillE, sSkillF, sSkillF800B)
        }

        private suspend fun initializerFindSelfHp(screenshot: Bitmap?): Block? {
//            val pf = Prefab.requirePrefab(Prefab.ITEM_PLAYER_HP)
//            if (pf != null) return pf.parseToBlock()

            if (screenshot == null) return null
            return withContext(Dispatchers.IO) {
                val hitSelfHp = setOf(
                    0xff85271f.toInt().rgb, 0xff591b15.toInt().rgb, 0xff611917.toInt().rgb,
                    0xff641a19.toInt().rgb, 0xff79201b.toInt().rgb, 0xff9a281e.toInt().rgb,
                    0xff6b1c1a.toInt().rgb, 0xff5e1817.toInt().rgb
                )
                fun findLeft(): Int? {
                    var x = 0
                    while(x < 400.pt) {
                        var y = 80.pt
                        while(y < 240.pt) {
                            val color = App.take().takePixel(screenshot, x, y)
                            val hit = hitSelfHp.containsSimilarColor(color)
                            if (hit) {
                                val check = Block("hp检测", x , y, 80.pt, 3.pt)
                                if (Analysis.hit(screenshot, check, hitSelfHp, 5.pt, 0.9f)) { // "hpCheck"
                                    return x //  - 3.pt // 最好刚好贴合，不然空血会漏
                                }
                            }
                            y += 1.pt
                        }
                        x += 1.pt
                    }
                    return null
                }
                fun findTop(left: Int?): Int? {
                    if (left == null) return null
                    var y = 80.pt
                    while(y < 240.pt) {
                        var x = left
                        while (x < left + 60.pt) {
                            val color = App.take().takePixel(screenshot, x, y)
                            val hit = hitSelfHp.containsSimilarColor(color)
                            if (hit) {
                                val check = Block("hp检测", x , y, 80.pt, 3.pt)
                                if (Analysis.hit(screenshot, check, hitSelfHp, 5.pt, 0.9f)) {
                                    return y
                                }
                            }
                            x += 3.pt
                        }
                        y += 1.pt
                    }
                    return null
                }
                val left = findLeft()
                val top = findTop(left)
                if (top != null && left != null) {
                    val height = 24.pt // 880:126   . 740:100
                    val width = (height * 1f / 100 * 740).toInt()
                    val right: Int = left + width
                    val bottom: Int = top + height
                    writeToDisk("GOSelfHpTop", top.toString())
                    writeToDisk("GOSelfHpBottom", bottom.toString())
                    writeToDisk("GOSelfHpLeft", left.toString())
                    writeToDisk("GOSelfHpRight", right.toString())
                    return@withContext Block(C.V_PLAYER_HP,left, top,right - left,bottom - top)
                }
                return@withContext null
            }
        }

        private suspend fun initializerFindsMap(screenshot: Bitmap?): Block? {
//            val pf = Prefab.requirePrefab(Prefab.ITEM_SMALL_MAP)
//            if (pf != null) return pf.parseToBlock()

            if (screenshot == null) return null
            return withContext(Dispatchers.IO) { // 475.pt, 314.pt
                val sMapTop = 9.pt //
                val sMapBottom = 225.pt //  小模式高216.宽度327
                val hitSelfInsMap1 = setOf(
                    0xffdbcc9f.toInt().rgb(10), 0xffdbc8a1.toInt().rgb(10),
                    0xffd8c59f.toInt().rgb(10), 0xffd8c3a7.toInt().rgb(10), // 微亮偏向黄
                    0xffdbcba0.toInt().rgb(10), // 地图闪任务进一步偏黄
                    0xff603f37.toInt().rgb(20), // 组队数字
                    0xff6d4e43.toInt().rgb(20), // 组队数字
                )
                val hitSelfInsMap2 = setOf(
                    0xffcbbda1.toInt().rgb(10), 0xffc5b09a.toInt().rgb(10),
                    0xffcbbda1.toInt().rgb(10), 0xffc5b09a.toInt().rgb(10), // 暗偏向灰
                    0xffc8b79e.toInt().rgb(10), // 地图闪任务进一步偏黄
                    0xff603f37.toInt().rgb(20), // 组队数字
                    0xff6d4e43.toInt().rgb(20), // 组队数字
                )
                val hitSelfInsMap = setOf(
                    0xffdbcc9f.toInt().rgb(10), 0xffdbc8a1.toInt().rgb(10),
                    0xffd8c59f.toInt().rgb(10), 0xffd8c3a7.toInt().rgb(10), // 微亮偏向黄
                    0xffdbcba0.toInt().rgb(10), // 地图闪任务进一步偏黄
                    0xffcbbda1.toInt().rgb(10), 0xffc5b09a.toInt().rgb(10),
                    0xffcbbda1.toInt().rgb(10), 0xffc5b09a.toInt().rgb(10), // 暗偏向灰
                    0xffc8b79e.toInt().rgb(10), // 地图闪任务进一步偏黄
                )
                val hitSelfInsMapAll = setOf(
                    0xffdbcc9f.toInt().rgb(10), 0xffdbc8a1.toInt().rgb(10),
                    0xffd8c59f.toInt().rgb(10), 0xffd8c3a7.toInt().rgb(10), // 微亮偏向黄
                    0xffdbcba0.toInt().rgb(10), // 地图闪任务进一步偏黄
                    0xffcbbda1.toInt().rgb(10), 0xffc5b09a.toInt().rgb(10),
                    0xffcbbda1.toInt().rgb(10), 0xffc5b09a.toInt().rgb(10), // 暗偏向灰
                    0xffc8b79e.toInt().rgb(10), // 地图闪任务进一步偏黄
                    0xff603f37.toInt().rgb(20), // 组队数字
                    0xff6d4e43.toInt().rgb(20), // 组队数字
                )

                val selfInsMapTop = 103.pt // 157.pt
                val selfInsMapBottom = 124.pt // 177.pt // 21
                val useLargeSMap = false

                if (!useLargeSMap) {
                    var x = phoneScreenWidth - 100.pt
                    val endX = phoneScreenWidth - 700.pt
                    while (x > endX) {
                        var y = selfInsMapTop
                        while (y < selfInsMapBottom) {
                            val color = App.take().takePixel(screenshot, x, y)
                            if (hitSelfInsMap.containsSimilarColor(color)) {
                                val check = Block("selfInsMap", x - 20.pt, selfInsMapTop, 20.pt, 20.pt)
                                val c1 = Analysis.hitAny(screenshot, check, hitSelfInsMap1, 2.pt)
                                val c2 = Analysis.hitAny(screenshot, check, hitSelfInsMap2, 2.pt)

                                if (c1 && c2) {
                                    if (Analysis.hit(screenshot, check, hitSelfInsMapAll, 2, 0.24f)) { // , "selfInsMap"
                                        val sMapHeight = sMapBottom - sMapTop
                                        //  // 475.pt, 314.pt -> 327.pt, 216.pt
                                        val sMapWidth = (sMapHeight / 640f * 970).toInt() // 大 (sMapHeight / 314f * 475).toInt()
                                        val sMapLeft = check.x - sMapWidth / 2
                                        val sMapRight = sMapLeft + sMapWidth
                                        writeToDisk("GOsMapTop", sMapTop.toString())
                                        writeToDisk("GOsMapBottom", sMapBottom.toString())
                                        writeToDisk("GOsMapLeft", sMapLeft.toString())
                                        writeToDisk("GOsMapRight", sMapRight.toString())
                                        return@withContext Block(C.SMALL_MAP, sMapLeft, sMapTop, sMapWidth, sMapHeight)
                                    }
                                }
                            }
                            y += 3.pt
                        }
                        x -= 1.pt
                    }
                }


                // 试试大地图初始化  + 78.pt //  78是看实际情况凑的.
                if (useLargeSMap) {
                    val selfInsMapTop2 =  157.pt
                    val selfInsMapBottom2 = 177.pt
                    var x = phoneScreenWidth - 100.pt
                    val endX = phoneScreenWidth - 700.pt
                    while (x > endX) {
                        var y = selfInsMapTop2
                        while (y < selfInsMapBottom2) {
                            val color = App.take().takePixel(screenshot, x, y)
                            if (hitSelfInsMap.containsSimilarColor(color)) {
                                val check = Block("selfInsMap", x - 20.pt, selfInsMapTop2, 20.pt, 20.pt)
                                val c1 = Analysis.hitAny(screenshot, check, hitSelfInsMap1, 2.pt)
                                val c2 = Analysis.hitAny(screenshot, check, hitSelfInsMap2, 2.pt)
                                if (c1 && c2) {
                                    if (Analysis.hit(screenshot, check, hitSelfInsMapAll, 2, 0.24f)) { // , "selfInsMap"
                                        val sMapHeight = sMapBottom - sMapTop
                                        //  // 475.pt, 314.pt -> 327.pt, 216.pt
                                        val sMapWidth = (sMapHeight / 640f * 970).toInt() // 大 (sMapHeight / 314f * 475).toInt()
                                        val sMapLeft = check.x - sMapWidth / 2  + 79.pt //  78是看实际情况凑的.
                                        val sMapRight = sMapLeft + sMapWidth
                                        if (sMapRight < phoneScreenWidth) {
                                            // x轴边界检查合格
                                            writeToDisk("GOsMapTop", sMapTop.toString())
                                            writeToDisk("GOsMapBottom", sMapBottom.toString())
                                            writeToDisk("GOsMapLeft", sMapLeft.toString())
                                            writeToDisk("GOsMapRight", sMapRight.toString())
                                            return@withContext Block(C.SMALL_MAP, sMapLeft, sMapTop, sMapWidth, sMapHeight)
                                        }

                                    }
                                }
                            }
                            y += 3.pt
                        }
                        x -= 1.pt
                    }
                }



                return@withContext null
            }
        }

        private suspend fun initializerFindMedicine(screenshot: Bitmap?): Block? {
//            val pf = Prefab.requirePrefab(Prefab.ITEM_MEDICINE)
//            if (pf != null) return pf.parseToBlock()

            if (screenshot == null) return null
            return withContext(Dispatchers.IO) {
                val rectMedicine = Block(C.V_MEDICINE, phoneScreenWidth - 500.pt, 300.pt, 500.pt, 350.pt)
                val hitMedicine =
                    setOf(0xfff11212.toInt().rgb(10), 0xffe91616.toInt().rgb(10), 0xffe30808.toInt().rgb(10))
                fun findTop(): Int? {
                    repeat(rectMedicine.height) { y ->
                        repeat(rectMedicine.width) { xx ->
                            val x = rectMedicine.width - 1 - xx // 从右边往左边扫描
                            val color = App.take().takePixel(screenshot, rectMedicine.left + x, rectMedicine.top + y)
                            val hit = hitMedicine.containsSimilarColor(color)
                            if (hit) return rectMedicine.top + y
                        }
                    }
                    return null
                }
                fun findRight(): Int? {
                    repeat(rectMedicine.width) { xx ->
                        val x = rectMedicine.width - 1 - xx // 从右边往左边扫描
                        repeat(rectMedicine.height) { y ->
                            val color = App.take().takePixel(screenshot, rectMedicine.left + x, rectMedicine.top + y)
                            val hit = hitMedicine.containsSimilarColor(color)
                            if (hit) return phoneScreenWidth - (rectMedicine.width - x) + 5.pt  // 向左边移动60pt定位到地图边缘
                        }
                    }
                    return null
                }
                val top: Int? = findTop()
                val right: Int? = findRight()
                if (top != null && right != null) {
                    val size = 95.pt
                    val left = right - size
                    val bottom = top + size

                    // 位置创建的时候atk. skillA等位置可能越界
                    if (right < 520.pt || top + 460.pt >= phoneScreenHeight) {
                        return@withContext null // 位置创建不出技能按钮
                    }

                    // 内缩23. (95-50)/2 // val medicine = Block(C.V_MEDICINE, medicineFall.right - 72.pt, medicineFall.top + 23.pt, 50.pt, 50.pt)
                    val s = 50.pt  + 40.pt
                    val t = top + 23.pt - 20.pt
                    val b = bottom - 23.pt + 20.pt
                    val l = left + 23.pt - 20.pt
                    val r = right - 23.pt + 20.pt
                    writeToDisk("GOMedicineTop2", t.toString())
                    writeToDisk("GOMedicineBottom2", b.toString())
                    writeToDisk("GOMedicineLeft2", l.toString())
                    writeToDisk("GOMedicineRight2", r.toString())
                    return@withContext Block(C.V_MEDICINE, l, t, s, s)
                }
                return@withContext null
            }
        }

        private suspend fun initializerFindMap(screenshotMap: Bitmap?): Block? {
            if (screenshotMap == null) return null
            return withContext(Dispatchers.IO) {
                fun findClose(): Block? {
                    val hitRed = setOf(
                        0xff6d2b15.toInt().rgb, 0xff7a3015.toInt().rgb,
                        0xff9a4216.toInt().rgb, 0xff863314.toInt().rgb,
                        0xff652510.toInt().rgb,
                    )
                    val hitWhite = setOf(
                        0xffffffe7.toInt().rgb, 0xffffffdd.toInt().rgb,
                        0xffeeeecf.toInt().rgb,
                    )
                    val hitContent = mutableSetOf<ColorDiffer>().apply {
                        addAll(hitRed)
                        addAll(hitWhite)
                    }
                    val closeCheckPart = Block("关闭检测",  phoneScreenWidth - 450.pt, 0.pt, 450.pt, 250.pt)
                    var x = closeCheckPart.right - 1
                    while (x > closeCheckPart.left) {
                        var y = closeCheckPart.top
                        while (y < closeCheckPart.bottom) {
                            val color = App.take().takePixel(screenshotMap, x, y)
                            val hit = hitRed.containsSimilarColor(color)
                            if (hit) {
                                val block = Block("close", x - 40.pt, y, 40.pt, 40.pt)
                                if (Analysis.hitAny(screenshotMap, block, hitWhite, 3.pt)) {
                                    // minPercent = 0.75206614 左右
                                    if (Analysis.hit(screenshotMap, block, hitContent, 4.pt, 0.5f)) {
                                        return block
                                    }
                                }
                            }
                            y += 2.pt
                        }
                        x -= 2.pt
                    }
                    return null
                }
                fun findBackLeft(): Int? {
                    val hitRed = setOf(
                        0xff6e3119.toInt().rgb, 0xff6b2e1a.toInt().rgb,
                        0xff682f16.toInt().rgb, 0xff703216.toInt().rgb,
                        0xff66301d.toInt().rgb, 0xff632817.toInt().rgb,
                    )
                    val hitWhite = setOf(
                        0xfffaf1d1.toInt().rgb, 0xfff4e9c8.toInt().rgb,
                    )
                    val hitContent = mutableSetOf<ColorDiffer>().apply {
                        addAll(hitRed)
                        addAll(hitWhite)
                    }
                    val backCheckPart = Block("返回检测", 0.pt, 0.pt, 340.pt, 150.pt)
                    var x = 0
                    while (x < backCheckPart.right) {
                        var y = backCheckPart.top
                        while (y < backCheckPart.bottom) {
                            val color = App.take().takePixel(screenshotMap, x, y)
                            val hit = hitRed.containsSimilarColor(color)
                            if (hit) {
                                val block = Block("closeL", x, y, 60.pt, 40.pt)
                                val hasPart1 = Analysis.hitAny(screenshotMap, block, hitWhite, 4.pt)
                                val hasPart2 = Analysis.hit(screenshotMap, block, hitContent, 2.pt, 0.5f) // , "checkBackL"
                                if (hasPart1 && hasPart2) return x
                            }
                            y += 8.pt
                        }
                        x += 8.pt
                    }
                    return null
                }
                fun findBlueBottom(closeRight: Int?): Int? {
                    if (closeRight == null) return null
                    val hitBlue = setOf(
                        0xff102792.toInt().rgb, 0xff11238c.toInt().rgb,
                        0xff152f9f.toInt().rgb, 0xff11238a.toInt().rgb,
                    )
                    val checkLeft = closeRight - 120.pt
                    val checkRight = checkLeft + 40.pt
                    val endY = phoneScreenHeight - 1
                    var y = phoneScreenHeight -300.pt
                    while (y < endY) {
                        var x = checkLeft
                        while (x < checkRight) {
                            val color = App.take().takePixel(screenshotMap, x, y)
                            if (hitBlue.containsSimilarColor(color)) {
                                return y
                            }
                            x += 2.pt
                        }
                        y += 2.pt
                    }
                    return null
                }

                val close = findClose()
                val backLeft = findBackLeft()
                val bottomBlue = findBlueBottom(close?.right)
                val top = if (close?.top != null) close.top + 44.pt else null
                val right = if (close?.right != null) close.right - 4.pt else null
                val left = if (backLeft != null) backLeft + 9.pt else null
                val bottom = if (bottomBlue != null) bottomBlue + 106.pt else null
                if (top != null && bottom != null && left != null && right != null) {
                    writeToDisk("GOMapTop", top.toString())
                    writeToDisk("GOMapBottom", bottom.toString())
                    writeToDisk("GOMapLeft", left.toString())
                    writeToDisk("GOMapRight", right.toString())
                    return@withContext Block("地图",left, top,right - left,bottom - top)
                }
                return@withContext null
            }
        }

//        private suspend fun saveFailureSS(reasonToFileName: String, ss: Bitmap) {
//            if (isDebug) {
//                val outputPath = File(FileUtils.externalCacheDir("initFailure"), "${reasonToFileName}_${System.currentTimeMillis().toTime("MMddHHmmss")}.jpg")
//                FileUtils.writeBitmapToDisk(outputPath, ss)
//            }
//        }
    }

}

private fun toast1(msg: String) {
    toast(msg)
    outputText(msg)
}

fun takeInitParam(key: String): Int? {
    val is720P = phoneDefaultDisplayPoint.x == 1280 && phoneDefaultDisplayPoint.y == 720 && phoneDensityDpi == 320
    val cache= if (is720P) {
        if (key == "GOSelfHpLeft")  10
        else if (key == "GOSelfHpRight")  135
        else if (key == "GOSelfHpTop")  93
        else if (key == "GOSelfHpBottom")  110
        else if (key == "GOsMapLeft")  983
        else if (key == "GOsMapRight")  1219
        else if (key == "GOsMapTop")  6
        else if (key == "GOsMapBottom")  162
        else if (key == "GOMedicineLeft2")  1149
        else if (key == "GOMedicineRight2")  1213
        else if (key == "GOMedicineTop2")  300
        else if (key == "GOMedicineBottom2")  364
        else null
    } else null

    cache?.also {
        printLog { "takeInitParam use 720P fixedCache $key $it" }
        return it
    }
    return readByDisk(key)?.toIntOrNull()
}

 fun aaa() {
//    val GOSelfHpLeft = readByDisk("GOSelfHpLeft")?.toIntOrNull()
//    val GOSelfHpRight = readByDisk("GOSelfHpRight")?.toIntOrNull()
//    val GOSelfHpTop = readByDisk("GOSelfHpTop")?.toIntOrNull()
//    val GOSelfHpBottom = readByDisk("GOSelfHpBottom")?.toIntOrNull()
//    val GOsMapLeft = readByDisk("GOsMapLeft")?.toIntOrNull()
//    val GOsMapRight = readByDisk("GOsMapRight")?.toIntOrNull()
//    val GOsMapTop = readByDisk("GOsMapTop")?.toIntOrNull()
//    val GOsMapBottom = readByDisk("GOsMapBottom")?.toIntOrNull()
//    val GOMedicineLeft2 = readByDisk("GOMedicineLeft2")?.toIntOrNull()
//    val GOMedicineRight2 = readByDisk("GOMedicineRight2")?.toIntOrNull()
//    val GOMedicineTop2 = readByDisk("GOMedicineTop2")?.toIntOrNull()
//    val GOMedicineBottom2 = readByDisk("GOMedicineBottom2")?.toIntOrNull()
//
//        printLog { "GOSelfHpLeft:$GOSelfHpLeft" } // 10
//        printLog { "GOSelfHpRight:$GOSelfHpRight" } // 135
//        printLog { "GOSelfHpTop:$GOSelfHpTop" }  // 93
//        printLog { "GOSelfHpBottom:$GOSelfHpBottom" } // 110
//        printLog { "GOsMapLeft:$GOsMapLeft" } // 983
//        printLog { "GOsMapRight:$GOsMapRight" } // 1219
//        printLog { "GOsMapTop:$GOsMapTop" } // 6
//        printLog { "GOsMapBottom:$GOsMapBottom" } // 162
//        printLog { "GOMedicineLeft2:$GOMedicineLeft2" } // 1149
//        printLog { "GOMedicineRight2:$GOMedicineRight2" } // 1213
//        printLog { "GOMedicineTop2:$GOMedicineTop2" } // 300
//        printLog { "GOMedicineBottom2:$GOMedicineBottom2" } // 364
}